/*
 * Treatment Record Handler
 */
/*
 * TreatmentRecordHandler.java
 * 
 * @version 1.0, March 15, 2011
 * CS 2340 Team 10: Cyberdyne Systems
 */
package edu.gatech.oad.cyberdoc.domain.action;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.JSONException;
import org.json.JSONObject;

import edu.gatech.oad.cyberdoc.domain.Admin;
import edu.gatech.oad.cyberdoc.domain.Doctor;
import edu.gatech.oad.cyberdoc.domain.Patient;
import edu.gatech.oad.cyberdoc.domain.Staff;
import edu.gatech.oad.cyberdoc.domain.TreatmentRecord;
import edu.gatech.oad.cyberdoc.domain.TreatmentRecordInfo;
import edu.gatech.oad.cyberdoc.domain.User;

/**
 * Handles all actions related to treatment records.
 * 
 * @author John Girata
 * @version 1.0, March 15, 2011
 */
public class TreatmentRecordHandler extends ActionHandler {
	/**
	 * The logger for this class.
	 */
	private static final Logger LOG;

	/**
	 * The action code this ActionHandler corresponds to.
	 */
	public static final String[] ACTION_CODES;

	static {
		ACTION_CODES = new String[] { "create_tr", "read_tr", "update_tr",
				"delete_tr" };
		LOG = Logger.getLogger("edu.gatech.oad.cyberdoc.action."
				+ "TreatmentRecordHandler");
	}

	/**
	 * Registers this ActionHandler with the Patient class to start receiving
	 * actions.
	 * 
	 * This class requires an array storing all of the possible time slots that
	 * an appointment may be registered.
	 */
	public TreatmentRecordHandler() {
		for (String code : ACTION_CODES) {
			Patient.addActionHandler(code,this);
			Staff.addActionHandler(code, this);
			Doctor.addActionHandler(code, this);
			Admin.addActionHandler(code, this);
		}
	}
	
	/**
	 * Reverses everything done by the constructor.
	 */
	@Override
	public void destroy() {
		Staff.removeActionHandler(this);
		Doctor.removeActionHandler(this);
		Admin.removeActionHandler(this);
	}

	/**
	 * Handles all commands related to TreatmentRecords
	 * 
	 * TODO Document this better
	 * 
	 * This method expects the following fields:
	 * 
	 * action: The specific action to be performed; one of: create_tr, read_tr,
	 * update_tr, delete_tr
	 * 
	 * id: The ID of the treatment record (if action is "read_tr", "update_tr",
	 * or "delete_tr")
	 * 
	 * datetime: The date and time this treatment record was added
	 * 
	 * patient_username: The patient associated with this treatment record
	 * 
	 * doctor_username: The doctor associated with this treatment record (C/U only)
	 * 
	 * staff_username: The staff member associated with this treatment record (C/U only)
	 * 
	 * doctorOrders: The doctor's orders
	 * 
	 * symptoms: The patient's symptoms
	 * 
	 * vitalsigns: The patient's vital signs
	 * 
	 * diagnosis: The doctor's diagnosis
	 * 
	 * This method will return the following fields:
	 * 
	 * next: The next frame to show the user
	 * 
	 * @param source
	 *            The user that called this method
	 * @param command
	 *            The command encoding the action
	 * @return The result of performing this action
	 * @throws ActionHandleException
	 *             If a required field does not exist in the command, or if the
	 *             usernames given for the patient and doctor are invalid
	 */
	@Override
	public JSONObject handle(User source, JSONObject command)
			throws ActionHandleException {
		JSONObject response = new JSONObject();

		// TODO Clean up this method
		try {
			final String action = command.getString("action");

			if (action.matches("create_tr|update_tr")) {
				response = createUpdateHandler(source, command);
			} else if (action.equals("read_tr")) {
				response = readHandler(command.getInt("id"));
			} else if (action.equals("delete_tr")) {
				response = deleteHandler(source, command);
			}
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Unexpected JSONException.", e);
			throw new ActionHandleException(e);
		}

		return response;
	}

	/**
	 * Handles read_tr actions.
	 * 
	 * @param id
	 *            The ID of the TreatmentRecord to read.
	 * @return A JSONObject encoding the requested TreatmentRecord
	 * @throws ActionHandleException
	 *             If the requested TreatmentRecord does not exist.
	 * @throws JSONException
	 *             If an error occurs while writing to the JSONObject
	 */
	public JSONObject readHandler(int id) throws ActionHandleException,
			JSONException {
		final TreatmentRecord record = TreatmentRecord.getTreatmentRecord(id);

		if (record == null) {
			throw new ActionHandleException("Treatment record with id=" + id
					+ " does not exist.");
		}

		final JSONObject response = record.toJSONObject();
		response.put("next", "view_tr");

		return response;
	}

	/**
	 * Handles create_tr and update_tr, as they require the same process with
	 * only a few minor differences.
	 * 
	 * @param source
	 *            The user that fired this action
	 * @param command
	 *            The command encoding the action
	 * @return The result of performing this action
	 * @throws ActionHandleException
	 *             or if the usernames given for the patient and doctor are
	 *             invalid
	 * @throws JSONException
	 *             If a required field does not exist in the command
	 */
	private JSONObject createUpdateHandler(User source,
			JSONObject command) throws ActionHandleException, JSONException {
		final Map<String, Object> map = new HashMap<String, Object>();
		final String action = command.getString("action");

		// 1. Find users
		try {
			// TODO Make these optional (as appropriate)
			map.put("patient", getUser(command, "patient", Patient.class));

			if (action.equals("create_tr")) {
				map.put("doctor", getUser(command, "doctor", Doctor.class));
				map.put("staff", getUser(command, "staff", Staff.class));
			}
		} catch (ClassCastException e) {
			LOG.log(Level.WARNING, "Bad username", e);
			throw new ActionHandleException(e);
		}

		// 2. Encode other information
		for (String key : TreatmentRecordInfo.KEYS) {
			if (!map.containsKey(key) && command.has(key)) {
				map.put(key, command.getString(key));
			}
		}

		TreatmentRecord record = null;
		TreatmentRecordInfo info = null;

		// 3. Find the treatment record (update_tr only)
		if (action.matches("update_tr")) {
			record = TreatmentRecord.getTreatmentRecord(command.getInt("id"));
			info = new TreatmentRecordInfo(record.getInfo(), map);
			record.setInfo(info);
		}

		info = new TreatmentRecordInfo(null, map);

		// 4. Create new treatment record (create_tr only)

		if (action.matches("create_tr")) {
			record = new TreatmentRecord(info);
		} else {
			record.setInfo(info);
		}

		return readHandler(record.getID());
	}

	/**
	 * Returns a user with the username defined in command and of the specified
	 * type.
	 * 
	 * @param command
	 *            The JSONObject containing the fields
	 * @param field
	 *            The field containing the username
	 * @param type
	 *            The expected type of User
	 * @return The User with the associated username
	 * @throws ActionHandleException
	 *             If no user exists with the requested username
	 * @throws JSONException
	 *             If field does not exist in command
	 */
	// TODO More thoroughly test this
	private User getUser(JSONObject command, String field, Class<?> type)
			throws ActionHandleException, JSONException {
		final User user = User.getUser(command.getString(field));

		if (user == null || !type.isInstance(user)) {
			throw new ActionHandleException("Invalid " + field + " username");
		}

		return user;
	}

	/**
	 * Handles delete_tr actions. After removing the treatment record, this
	 * method calls UserHandler.handle() to show the user overview.
	 * 
	 * @param source
	 *            The user performing this action
	 * @param command
	 *            The command from the user
	 * @return The response to the action
	 * @throws ActionHandleException
	 *             If an ActionHandleException is thrown by UserHandler.handle()
	 * @throws JSONException
	 *             If an error occurs while writing to the JSONObject
	 */
	private JSONObject deleteHandler(User source, JSONObject command)
			throws ActionHandleException, JSONException {
		final int id = command.getInt("id");
		final TreatmentRecord record = TreatmentRecord.getTreatmentRecord(id);
		record.destroy();

		final JSONObject request = new JSONObject();
		request.put("user",
				((Patient) record.getField("patient")).getUsername());
		return pass("read_user", request);
	}

	/**
	 * Returns a string representation of this class.
	 * 
	 * @return A string representation of this class.
	 */
	@Override
	public String toString() {
		return "TreatmentRecordHandler (action=\""
				+ Arrays.toString(ACTION_CODES) + "\")";
	}
}
